#ifndef     __FRAME_H
#define     __FRAME_H

#include "../utils/alg.h"
#include "../serialize/encode.h"
#include "../serialize/decode.h"

namespace conn {

namespace proto {

constexpr uint8_t HEAD0 = 0xE1;
constexpr uint8_t HEAD1 = 0x1E;
constexpr uint8_t END = 0xEF;

namespace pub {

constexpr uint8_t NOTIFY_LOG = 0x00;
constexpr uint8_t QUERY_TASK_INFO = 0x00;

constexpr uint8_t OTA_START = 0x01;
constexpr uint8_t OTA_TRANSLATE = 0x02;
constexpr uint8_t OTA_COMPLETE = 0x03;

}

namespace cargo {

constexpr uint8_t STATUS_UPLOAD = 0x11;
constexpr uint8_t CARGO_CTRL = 0x11;
constexpr uint8_t PICK_CTRL = 0x12;
constexpr uint8_t BOX_MOTOR_CTRL = 0x13;
constexpr uint8_t COMP_CTRL = 0x14;
constexpr uint8_t SCAN = 0x15;
constexpr uint8_t CARGO_TEST = 0x16;
constexpr uint8_t CARGO_PICK = 0x17;
constexpr uint8_t CARGO_SET_ARG = 0x18;

constexpr uint8_t QUERY_CARGO_ARG = 0x01;

}

namespace heat {

constexpr uint8_t STATUS_UPLOAD = 0x11;

constexpr uint8_t PARTS_CTRL = 0x11;
constexpr uint8_t FILM_CTRL = 0x12;
constexpr uint8_t STIR_CAP_CTRL = 0x13;
constexpr uint8_t HOLD_CTRL = 0x14;
constexpr uint8_t STIR_MOTOR_CTRL = 0x15;
constexpr uint8_t SET_DRAW = 0x16;
constexpr uint8_t SET_HEAT = 0x17;
constexpr uint8_t SET_FLOW = 0x18;
constexpr uint8_t HOT_DRAW = 0x19;
constexpr uint8_t HEAT_DRAW_CTRL = 0x1A;

constexpr uint8_t QUERY_DRAW = 0x11;
constexpr uint8_t QUERY_HEAT = 0x12;
constexpr uint8_t QUERY_FLOW = 0x13;

constexpr uint8_t BEGIN_STEAM = 0x14;
constexpr uint8_t OPEN_STEAM = 0x15;
constexpr uint8_t CLOSE_STEAM = 0x16;
}

namespace pearl {

constexpr uint8_t STATUS_UPLOAD = 0x11;
constexpr uint8_t MOVE_CTRL = 0x11;
constexpr uint8_t UPDOWN_CTRL = 0x12;
constexpr uint8_t DUMP_CTRL = 0x13;


}

namespace ice {

constexpr uint8_t STATUS_UPLOAD = 0x11;
constexpr uint8_t PARTS_CTRL = 0x11;
constexpr uint8_t DRAW_MAKE_CTRL = 0x12;
constexpr uint8_t DROP_ICE_CTRL = 0x13;

}

namespace main {

constexpr uint8_t STATUS_UPLOAD = 0x11;
constexpr uint8_t PARTS_CTRL = 0x11;
constexpr uint8_t PICK_DOOR_CTRL = 0x12;
constexpr uint8_t DROP_CUP_CTRL = 0x13;
constexpr uint8_t FILM_CTRL = 0x14;
constexpr uint8_t LOCK_CTRL = 0x15;
constexpr uint8_t ROBOT_CTRL = 0x16;

}

}

struct Frame {

    const uint8_t *buf;
    uint8_t len;

    inline Frame(const uint8_t *b, uint8_t l) : buf(b), len(l) {}
    inline Frame() : buf(nullptr), len(0) {}

    template <typename... Ts>
    Error parse(Ts&&... ts) const {
        return serialize::decode(buf + 8, len - 8, std::forward<Ts>(ts)...);    
    }

    template <typename... Ts>
    Error parse_query(Ts&&... ts) const {
        return serialize::decode(buf + 7, len - 7, std::forward<Ts>(ts)...);
    }

    inline uint8_t dst() const {
        return buf[3];
    }

    inline uint8_t src() const {
        return buf[4];
    }

    inline uint8_t type() const {
        return buf[5];
    }

    inline uint8_t seq_cmd() const {
        return buf[6];
    }

    inline uint8_t cmd() const {
        return buf[7];
    }
};

namespace addr {

constexpr uint8_t IPC = 0;
constexpr uint8_t MAIN = 1;
constexpr uint8_t CARGO = 2;
constexpr uint8_t HEAT = 3;
constexpr uint8_t PEARL = 4;
constexpr uint8_t ICE = 5;

#if defined (APP_MAIN)
constexpr uint8_t SELF = MAIN;
#elif defined (APP_CARGO)
constexpr uint8_t SELF = CARGO;
#elif defined (APP_HEAT) 
constexpr uint8_t SELF = HEAT;
#elif defined (APP_PEARL)
constexpr uint8_t SELF = PEARL;
#elif defined (APP_ICE)
constexpr uint8_t SELF = ICE;
#endif


}

namespace type {

constexpr uint8_t ACK = 0;
constexpr uint8_t PING = 1;
constexpr uint8_t PONG = 2;
constexpr uint8_t QUERY = 3;
constexpr uint8_t RESULT = 4;
constexpr uint8_t SESSION = 5;
constexpr uint8_t REQUEST = 6;
constexpr uint8_t RESPONSE = 7;
constexpr uint8_t FAST_REQ = 8;
constexpr uint8_t FAST_RES = 9;
constexpr uint8_t NOTIFY = 10;

}

bool is_frame_valid(const uint8_t *buf, usize_t len);

// proto data begin 6
template <uint8_t TYPE>
usize_t make_proto_data(uint8_t *buf, uint8_t dst, usize_t data_len) {
    buf[0] = proto::HEAD0;
    buf[1] = proto::HEAD1;
    buf[2] = data_len + 8;
    buf[3] = dst;
    buf[4] = addr::SELF;
    buf[5] = TYPE;
    buf[6 + data_len] = alg::xor_sum(buf + 3, data_len + 3);
    buf[7 + data_len] = proto::END;
    return data_len + 8;
}

template <uint8_t TYPE, typename... Ts>
inline usize_t make_any(uint8_t *buf, uint8_t dst, Ts&&... ts) {
    usize_t data_len = serialize::encode(buf + 6, std::forward<Ts>(ts)...);
    usize_t len = make_proto_data<TYPE>(buf, dst, data_len);
    return len;
}

}


#endif
